﻿using System;
using System.Linq;
using System.Reflection;

namespace NRex.Helpers
{
    /// <summary>
    /// Resolves unresolved assemblies by trying to load them from internal resources.
    /// When the CLR indicates that it cannot resolve the assembly Foo, this class will
    /// look into the executing assembly resources to find resource items which name ends
    /// with Foo.dll. It'll try to build an Assembly from the resource item for each match
    /// and returns at the first successful.
    /// 
    /// <code>
    /// static Program()
    /// {
    ///     new EmbeddedAssemblyResolver().Activate();
    /// }
    /// </code>
    /// </summary>
    internal class EmbeddedAssemblyResolver
    {
        /// <summary>
        /// Activates the resolver.
        /// Causes internal event subscription.
        /// </summary>
        public void Activate()
        {
            AppDomain.CurrentDomain.AssemblyResolve +=
                (sender, args) =>
                {
                    var resourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
                    var assemblyNameToResolve = string.Format("{0}.dll", new AssemblyName(args.Name).Name);

                    foreach (var resourceName in resourceNames.Where(name => name.EndsWith(assemblyNameToResolve)))
                    {
                        using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                        {
                            if (stream == null) continue;
                            var assemblyData = new Byte[stream.Length];
                            stream.Read(assemblyData, 0, assemblyData.Length);
                            return Assembly.Load(assemblyData);
                        }
                    }

                    return null;
                };
        }
    }
}